﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.Threading;

namespace Cdp
{
    public class CdpClient
    {
        private AsyncUdpClient client;
        private CdpEndPoint remote;

        public event CdpEventHandler OnConnected;
        public event CdpEventHandler OnReceived;
        public event CdpEventHandler OnSent;
        public event CdpEventHandler OnDisconnected;

        public CdpClient(int bufferSize)
        {
            client = new AsyncUdpClient(bufferSize);
            remote = new CdpEndPoint();
            remote.IsConnected = false;
            remote.MTU = 1024;
        }

        public bool IsConnected()
        {
            return remote.IsConnected;
        }

        public int GetRTT()
        {
            return remote.RTT;
        }

        public short GetMTU()
        {
            return remote.MTU;
        }

        public void Connect(IPEndPoint remoteEndPoint)
        {
            remote.RemoteEndPoint = remoteEndPoint;
            client.Connect(remoteEndPoint);
            client.OnReceived += Client_OnReceived;
            CdpMessage msg = new CdpMessage(MessageType.Synchronize);
            msg.SequenceNumber = remote.MTU;
            CdpDatagram data = msg.GenerateCdpDatagram(remote.RemoteEndPoint);
            client.SendAsync(data);
        }

        public void Send(CdpDatagram data)
        {

        }

        public void Close()
        {

        }

        private void Client_OnReceived(object sender, CdpDatagram data)
        {
            if (data.RemoteEndPoint.Equals(remote.RemoteEndPoint))
            {
                CdpMessage msg = CdpMessage.Parse(data.Buffer, data.Offset, data.BytesTransferred);
                if (msg != null)
                {
                    switch (msg.Type)
                    {
                        case MessageType.HeartBeat:
                            remote.IsConnected = true;
                            break;
                        case MessageType.RoundTripTime:
                            if (msg.IsAcknowledge)
                            {

                            }
                            else
                            {
                                msg.SequenceNumber = remote.Generater.GetPSequence();
                                CdpDatagram ack = msg.GenerateCdpDatagram(remote.RemoteEndPoint);
                                client.SendAsync(ack);
                            }
                            break;
                        case MessageType.Synchronize:
                            if(msg.IsAcknowledge)
                            {
                                remote.IsConnected = true;
                                remote.MTU = msg.SequenceNumber > remote.MTU ? remote.MTU : msg.SequenceNumber;
                                OnConnected?.Invoke(this, data);
                            }
                            else
                            {

                            }
                            break;
                        case MessageType.Finish:
                            remote.IsConnected = false;
                            OnDisconnected?.Invoke(this, null);
                            break;
                        case MessageType.Pull:
                            break;
                        case MessageType.Push:
                            CdpDatagram var = new CdpDatagram();
                            var.Buffer = msg.Content;
                            var.Offset = 0;
                            var.BytesTransferred = var.Buffer.Length;
                            var.RemoteEndPoint = data.RemoteEndPoint;
                            OnReceived?.Invoke(this, var);
                            break;
                        default:
                            break;
                    }
                }
            }
        }
    }
}
